﻿using System;
using System.Collections;

namespace Testy
{

    public delegate void TreeVisitor( int nodeData );

    public interface ITraversable
    {
        void Traverse( TreeVisitor visitor );
    }

    public class Tree : ITraversable
    {
        private const int FIRST_ELEMENT = 0;
        private int data;
        protected IList children = new ArrayList();

        public int Data
        {
            get
            {
                return data;
            }
            set
            {
                data = value;
            }
        }

        public int ChildCount 
        {
            get
            {
                return children.Count;
            }
        }

        public Tree( int data )
        {
            this.data = data;
        }

        public bool GetFirstChild(out int firstElement)
        {
Trace.Trace.TraceEnter( "Testy.Tree.GetFirstChild" );

            if ( children.Count > FIRST_ELEMENT )
            {
                firstElement = (Tree)children[FIRST_ELEMENT];
                {
bool __returnValue = true;
Trace.Trace.TraceLeave( "Testy.Tree.GetFirstChild", __returnValue, new Trace.ParameterInfo( Trace.ParameterType.Out, "firstElement", firstElement ) );
return __returnValue;
}
            }
            else
            {
                firstElement = int.MinValue;
                {
bool __returnValue = false;
Trace.Trace.TraceLeave( "Testy.Tree.GetFirstChild", __returnValue, new Trace.ParameterInfo( Trace.ParameterType.Out, "firstElement", firstElement ) );
return __returnValue;
}
            }
        }

        public virtual void AddChild( int data )
        {
Trace.Trace.TraceEnter( "Testy.Tree.AddChild", new Trace.ParameterInfo( Trace.ParameterType.In, "data", data ) );

            children.Add( new Tree( data ) );
        
Trace.Trace.TraceLeave( "Testy.Tree.AddChild" );
}

        public virtual void AddChild( params int[] data )
        {
Trace.Trace.TraceEnter( "Testy.Tree.AddChild", new Trace.ParameterInfo( Trace.ParameterType.Params, "data", data ) );

            foreach ( int item in data )
            {
                AddChild( item );
            }
        
Trace.Trace.TraceLeave( "Testy.Tree.AddChild" );
}

        public Tree this[int i]
        {
            get
            {
                return (Tree)children[i];
            }
            set
            {
                children[i] = value;
            }
        }

        public void Traverse( TreeVisitor visitor )
        {
Trace.Trace.TraceEnter( "Testy.Tree.Traverse", new Trace.ParameterInfo( Trace.ParameterType.In, "visitor", visitor ) );

            visitor( data );
            foreach ( Tree kid in children )
                Traverse( kid, visitor );
        
Trace.Trace.TraceLeave( "Testy.Tree.Traverse" );
}

        private void Traverse( Tree node, TreeVisitor visitor )
        {
Trace.Trace.TraceEnter( "Testy.Tree.Traverse", new Trace.ParameterInfo( Trace.ParameterType.In, "node", node ), new Trace.ParameterInfo( Trace.ParameterType.In, "visitor", visitor ) );

            if ( node == null )
                {
Trace.Trace.TraceLeave( "Testy.Tree.Traverse" );
return;
}

            visitor( node.data );
            foreach ( Tree kid in node.children )
                Traverse( kid, visitor );
        
Trace.Trace.TraceLeave( "Testy.Tree.Traverse" );
}

        ~Tree()
        {
            Console.WriteLine("Destruktor " + ToString());
        }

        public static Tree operator +(Tree tree1, Tree tree2)
        {
            if ( tree1 == null )
                throw new ArgumentNullException( "tree1" );
            if ( tree1 == null )
                throw new ArgumentNullException( "tree1" );

            Tree result = new Tree( tree1.Data + tree2.Data );
            foreach ( Tree tree in tree1.children )
            {
                result.AddChild( tree.Data );
            }
            foreach ( Tree tree in tree2.children )
            {
                result.AddChild( tree.Data );
            }
            return result;
        }

        public static implicit operator int(Tree tree)
        {
            return tree.Data;
        }

        public override string ToString()
        {
Trace.Trace.TraceEnter( "Testy.Tree.ToString" );

            {
string __returnValue = string.Format( "Drzewo o wartosci {0}", data );
Trace.Trace.TraceLeave( "Testy.Tree.ToString", __returnValue );
return __returnValue;
}
        }
    }

    public delegate void AddedChildEventHandler( object sender, Tree child, int position );

    public class SuperTree : Tree
    {
        public SuperTree( int data )
            : base( data )
        {
        }

        public override void AddChild( int data )
        {
Trace.Trace.TraceEnter( "Testy.SuperTree.AddChild", new Trace.ParameterInfo( Trace.ParameterType.In, "data", data ) );

            base.AddChild( data );
            if ( AddedChild != null )
            {
                AddedChild( this, (Tree)children[children.Count - 1], children.Count - 1 );
            }
        
Trace.Trace.TraceLeave( "Testy.SuperTree.AddChild" );
}

        public event AddedChildEventHandler AddedChild;
    }
}
